package bluecove;

import config.ConsoleUtil;
import config.SimpleProperties;

import javax.microedition.io.Connector;
import javax.obex.ClientSession;
import javax.obex.HeaderSet;
import javax.obex.Operation;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Class description...
 *
 * @author mvu
 */
public class BlueSync implements Device{
  private static String SERVICE_URL = "btgoep://001813107B02:7;authenticate=false;encrypt=false;master=false";
  public static String DEFAULT_PATH = "Phone memory/Other";

  public static void main(String[] args) {
    sync(DEFAULT_PATH + "/Mac", "/Users/mvu/Desktop/810i/");
    // delete("Phone memory/Other", "quotes.html");
    // send("Phone memory/Other", "test.txt", "Some text");
    ConsoleUtil.end();
  }

  public static void sync(String path, String file) {
    try {
      ClientSession conn = (ClientSession) Connector.open(SERVICE_URL);
      setHeaderToFolderBrowsing(conn);
      goToPath(path, conn);

      //Send the file
      FilenameFilter filter = new FilenameFilter() {
        public boolean accept(File file, String name) {
          return name.endsWith(".html");
        }
      };

      SimpleProperties myProperties = new SimpleProperties();
      long lastModified = myProperties.getLastModified();

      // delete obsolete files
      File localFolder = new File(file);
      File[] localFiles = localFolder.listFiles(filter);
      List<String> remoteFiles = list(conn);
      for (File local : localFiles) {
        String fileName = local.getName();
        boolean removed = remoteFiles.remove(fileName);
        if (removed && local.lastModified() > lastModified) {
          deleteFile(conn, fileName);
          addFile(conn, local);
        } else if (!removed) {
          addFile(conn, local);
        }
      }
      for (String currentFile : remoteFiles) {
        deleteFile(conn, currentFile);
      }

      conn.disconnect(conn.createHeaderSet());
      conn.close();
      myProperties.setLastModified(System.currentTimeMillis());
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  private static void addFile(ClientSession conn, File local) throws IOException {
    HeaderSet header;
    header = conn.createHeaderSet();
    String fileName = local.getName();
    header.setHeader(HeaderSet.NAME, fileName);
    ConsoleUtil.debug("sending " + fileName);
    Operation op = conn.put(header);
    OutputStream os = op.openOutputStream();
    InputStream is = new FileInputStream(local);
    byte[] b = new byte[400];
    int r;
    while ((r = is.read(b)) > 0) {
      os.write(b, 0, r);
    }
    is.close();
    os.close();
    op.close();
  }

  private static void deleteFile(ClientSession conn, String fileName) throws IOException {
    HeaderSet header;
    header = conn.createHeaderSet();
    header.setHeader(HeaderSet.NAME, fileName);
    ConsoleUtil.debug("deleting " + fileName);
    conn.delete(header);
  }

  public static boolean send(String path, String fileName, String text) {
    try {
      ConsoleUtil.debug("Sending " + fileName);
      ClientSession conn = (ClientSession) Connector.open(SERVICE_URL);
      setHeaderToFolderBrowsing(conn);
      goToPath(path, conn);

      sendFile(fileName, text, conn);

      conn.disconnect(conn.createHeaderSet());
      conn.close();
      ConsoleUtil.debug("Finish sending " + fileName);
      return true;
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }
  }

  private static void sendFile(String fileName, String text, ClientSession conn) throws IOException {
    ConsoleUtil.debug("Sending " + fileName);
    HeaderSet header;
    // delete file on device
    header = conn.createHeaderSet();
    header.setHeader(HeaderSet.NAME, fileName);
    conn.delete(header);

    // copy new file to device
    header = conn.createHeaderSet();
    header.setHeader(HeaderSet.NAME, fileName);
    Operation op = conn.put(header);
    OutputStream os = op.openOutputStream();
    byte[] b = text.getBytes();
    int r = b.length;
    os.write(b, 0, r);
    os.close();
    op.close();
    ConsoleUtil.debug("Done");
  }

  public static List<String> list(ClientSession conn) {
    List<String> list = new ArrayList<String>();
    try {
      //Retreive the contents of the folder
      HeaderSet header;
      header = conn.createHeaderSet();
      header.setHeader(HeaderSet.TYPE, "x-obex/folder-listing");
      Operation op = conn.get(header);
      BufferedReader br = new BufferedReader(new InputStreamReader(op.openInputStream()));
      String line;
      String xmlString = "";
      String PREFIX = "<file name=\"";
      while ((line = br.readLine()) != null) {
        xmlString = xmlString + line + "\n";
        int i = line.indexOf(PREFIX);
        if (i > -1) {
          int begin = i + PREFIX.length();
          int j = line.indexOf("\"", begin);
          if (j > -1) {
            list.add(line.substring(begin, j));
          }
        }
      }
      br.close();
      op.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return list;
  }

  public static void read(String path, String file) {
    try {
      ClientSession conn = (ClientSession) Connector.open(SERVICE_URL);
      setHeaderToFolderBrowsing(conn);
      goToPath(path, conn);

      //Retreive the file
      HeaderSet header = conn.createHeaderSet();
      header.setHeader(HeaderSet.NAME, file);
      Operation op = conn.get(header);
      InputStream is = op.openInputStream();
      File f = new File(file);
      FileOutputStream fos = new FileOutputStream(f);
      byte b[] = new byte[1000];
      int len;
      while (is.available() > 0 && (len = is.read(b)) > 0) {
        fos.write(b, 0, len);
      }
      fos.close();
      is.close();
      System.out.println("File stored in: " + f.getAbsolutePath());
      conn.disconnect(conn.createHeaderSet());
      conn.close();
    } catch (Exception e) {
      System.out.println("Exception " + e.toString());
    }
  }

  public static void delete(String path, String file) {
    try {
      ClientSession conn = (ClientSession) Connector.open(SERVICE_URL);
      setHeaderToFolderBrowsing(conn);
      goToPath(path, conn);

      //Retreive the file
      HeaderSet header = conn.createHeaderSet();
      header.setHeader(HeaderSet.NAME, file);
      conn.delete(header);
      conn.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  private static void setHeaderToFolderBrowsing(ClientSession conn) throws IOException {
    //Target Header must be set to the Folder Browsing UUID:
    //F9EC7BC4-953C-11D2-984E-525400DC9E09.
    byte[] FBUiid = {(byte) 0xF9, (byte) 0xEC, (byte) 0x7B, (byte) 0xC4, (byte) 0x95, (byte) 0x3C, (byte) 0x11, (byte) 0xD2,
            (byte) 0x98, (byte) 0x4E, (byte) 0x52, (byte) 0x54, (byte) 0x00, (byte) 0xDC, (byte) 0x9E, (byte) 0x09};
    //Connect
    HeaderSet header = conn.createHeaderSet();
    header.setHeader(HeaderSet.TARGET, FBUiid);
    conn.connect(header);
  }

  private static void goToPath(String path, ClientSession conn) throws IOException {
    HeaderSet header;
    String[] folders = path.split("/");
    //Go the desired folder
    for (String folder : folders) {
      header = conn.createHeaderSet();
      header.setHeader(HeaderSet.NAME, folder);
      conn.setPath(header, false, false);
    }
  }

  public void receive(Map<String, String> files) throws IOException {
    ClientSession conn = (ClientSession) Connector.open(SERVICE_URL);
    setHeaderToFolderBrowsing(conn);
    goToPath(DEFAULT_PATH, conn);

    for(Map.Entry<String,String> entry : files.entrySet()){
      sendFile(entry.getKey() + ".html", entry.getValue(), conn);
    }

    conn.disconnect(conn.createHeaderSet());
    conn.close();
  }
}
